#include "precompiled.h"
#pragma hdrstop

#include <stdlib.h>

#if (INF_PLATFORM == PLATFORM_IOS)
#include "sys/malloc.h" // mac os x
#else
#include <malloc.h> // linux, windows
#endif

namespace inf {
    //StrPool*		Dict::get_global_keys()	= NULL;
    //StrPool*		Dict::get_global_values()	= NULL;

    /*
    ================
    Dict::operator=

    clear existing key/value pairs and copy all key/value pairs from other
    ================
    */
    Dict &Dict::operator=( const Dict &other ) {
        int i;

        // check for assignment to self
        if ( this == &other ) {
            return *this;
        }

        clear();

        args = other.args;
        argHash = other.argHash;

        for ( i = 0; i < args.size(); i++ ) {
            args[ i ].key	= get_global_keys()->copy_string( args[i].key );
            args[ i ].value	= get_global_values()->copy_string( args[i].value );
        }

        return *this;
    }

    /*
    ================
    Dict::copy

    copy all key value pairs without removing existing key/value pairs not present in the other dict
    ================
    */
    void Dict::copy( const Dict &other ) {
        int i, n, *found;
        KeyValue kv;

        // check for assignment to self
        if ( this == &other ) {
            return;
        }

        n = other.args.size();

        if ( args.size() ) {
            found = (int *) _alloca( other.args.size() * sizeof( int ) );	// _alloca is CRT function
            for ( i = 0; i < n; i++ ) {
                found[i] = find_key_index( other.args[i].get_key() );
            }
        } else {
            found = NULL;
        }

        for ( i = 0; i < n; i++ ) {
            if ( found && found[i] != -1 ) {
                // first set the new value and then free the old value to allow proper self copying
                const PoolStr *oldValue = args[found[i]].value;
                args[found[i]].value = get_global_values()->copy_string( other.args[i].value );
                get_global_values()->free_string( oldValue );
            } else {
                kv.key = get_global_keys()->copy_string( other.args[i].key );
                kv.value = get_global_values()->copy_string( other.args[i].value );
                argHash.add( argHash.generate_key( kv.get_key(), false ), args.append( kv ) );
            }
        }
    }

    /*
    ================
    Dict::transfer_keyvalues

    clear existing key/value pairs and transfer key/value pairs from other
    ================
    */
    void Dict::transfer_keyvalues( Dict &other ) {
        int i, n;

        if ( this == &other ) {
            return;
        }

        clear();

        n = other.args.size();
        args.ready_num( n );
        for ( i = 0; i < n; i++ ) {
            args[i].key = other.args[i].key;
            args[i].value = other.args[i].value;
        }
        argHash = other.argHash;

        other.args.clear();
        other.argHash.free();
    }

    /*
    ================
    Dict::Parse
    ================
    */
    //bool Dict::Parse( idLexer &parser ) {
    //	idToken	token;
    //	idToken	token2;
    //	bool	errors;
    //
    //	errors = false;
    //
    //	if ( !parser.expectTokenString( "{" ) ) {
    //		return false;
    //	}
    //
    //	parser.ReadToken( &token );
    //	while( ( token.type != TT_PUNCTUATION ) || ( token != "}" ) ) {
    //		if ( token.type != TT_STRING ) {
    //			parser.error( "expected quoted string, but found '%s'", token.c_str() );
    //			break;
    //		}
    //
    //		if ( !parser.ReadToken( &token2 ) ) {
    //			parser.error( "Unexpected end of file" );
    //			break;
    //		}
    //
    //		if ( find_key( token ) ) {
    //			parser.warning( "'%s' already defined", token.c_str() );
    //			errors = true;
    //		}
    //		set( token, token2 );
    //
    //		if ( !parser.ReadToken( &token ) ) {
    //			parser.error( "Unexpected end of file" );
    //			break;
    //		}
    //	}
    //
    //	return !errors;
    //}

    /*
    ================
    Dict::Parse
    ================
    */
    //bool Dict::Parse( idParser &parser ) {
    //	idToken	token;
    //	idToken	token2;
    //	bool	errors;
    //
    //	errors = false;
    //
    //	if ( !parser.expectTokenString( "{" ) ) {
    //		return false;
    //	}
    //
    //	parser.ReadToken( &token );
    //	while( ( token.type != TT_PUNCTUATION ) || ( token != "}" ) ) {
    //		if ( token.type != TT_STRING ) {
    //			parser.error( "expected quoted string, but found '%s'", token.c_str() );
    //			break;
    //		}
    //
    //		if ( !parser.ReadToken( &token2 ) ) {
    //			parser.error( "Unexpected end of file" );
    //			break;
    //		}
    //
    //		if ( find_key( token ) ) {
    //			parser.warning( "'%s' already defined", token.c_str() );
    //			errors = true;
    //		}
    //		set( token, token2 );
    //
    //		if ( !parser.ReadToken( &token ) ) {
    //			parser.error( "Unexpected end of file" );
    //			break;
    //		}
    //	}
    //
    //	return !errors;
    //}

    /*
    ================
    Dict::set_defaults
    ================
    */
    void Dict::set_defaults( const Dict *dict ) {
        int i, n;
        const KeyValue *kv, *def;
        KeyValue newkv;

        n = dict->args.size();
        for( i = 0; i < n; i++ ) {
            def = &dict->args[i];
            kv = find_key( def->get_key() );
            if ( !kv ) {
                newkv.key = get_global_keys()->copy_string( def->key );
                newkv.value = get_global_values()->copy_string( def->value );
                argHash.add( argHash.generate_key( newkv.get_key(), false ), args.append( newkv ) );
            }
        }
    }

    /*
    ================
    Dict::clear
    ================
    */
    void Dict::clear( void ) {
        int i;

        for( i = 0; i < args.size(); i++ ) {
            get_global_keys()->free_string( args[i].key );
            get_global_values()->free_string( args[i].value );
        }

        args.clear();
        argHash.free();
    }

    /*
    ================
    Dict::print
    ================
    */
    void Dict::print() const {
        int i;
        int n;

        n = args.size();
        for( i = 0; i < n; i++ ) {
            printf( "%s = %s\n", args[i].get_key().c_str(), args[i].get_value().c_str() );
        }
    }

    int KeyCompare( const KeyValue *a, const KeyValue *b ) {
        return Str::cmp( a->get_key(), b->get_key() );
    }

    /*
    ================
    Dict::Checksum
    ================
    */
    //unsigned int Dict::Checksum( void ) const {
    //	unsigned long ret;
    //	int i, n;
    //
    //	List<KeyValue> sorted = args;
    //	sorted.sort( KeyCompare );
    //	n = sorted.size();
    //	CRC32_InitChecksum( ret );
    //	for( i = 0; i < n; i++ ) {
    //		CRC32_UpdateChecksum( ret, sorted[i].get_key().c_str(), sorted[i].get_key().length() );
    //		CRC32_UpdateChecksum( ret, sorted[i].get_value().c_str(), sorted[i].get_value().length() );
    //	}
    //	CRC32_FinishChecksum( ret );
    //	return ret;
    //}

    /*
    ================
    Dict::allocated
    ================
    */
    size_t Dict::allocated( void ) const {
        int		i;
        size_t	size;

        size = args.allocated() + argHash.allocated();
        for( i = 0; i < args.size(); i++ ) {
            size += args[i].total_size();
        }

        return size;
    }

    /*
    ================
    Dict::set
    ================
    */
    bool Dict::set( const char *key, const char *value ) {
        int i;
        KeyValue kv;

        assert( key );

        /*	if ( key == NULL || key[0] == '\0' ) {
        return;
        }*/

        i = find_key_index( key );
        if ( i != -1 ) {
            // allocstring is more expensive than this simple check
            if( !args[i].value->cmp( value, false ) ) {
                return false;
            }

            // first set the new value and then free the old value to allow proper self copying
            const PoolStr *oldValue = args[i].value;
            args[i].value = get_global_values()->alloc_string( value );
            get_global_values()->free_string( oldValue );
        } else {
            kv.key = get_global_keys()->alloc_string( key );
            kv.value = get_global_values()->alloc_string( value );
            argHash.add( argHash.generate_key( kv.get_key(), false ), args.append( kv ) );
        }

        return true;
    }

    /*
    ================
    Dict::set
    ================
    */
    bool Dict::set( int index, const char *value ) {
        assert( index >= 0 && index < args.size() );

        // allocstring is more expensive than this simple check
        if( !args[ index ].value->cmp( value, false ) ) {
            return false;
        }

        // first set the new value and then free the old value to allow proper self copying
        const PoolStr *oldValue = args[index].value;
        args[index].value = get_global_values()->alloc_string( value );
        get_global_values()->free_string( oldValue );

        return true;
    }

    /*
    ================
    Dict::get_float
    ================
    */
    bool Dict::get_float( const char *key, const char *defaultString, float &out ) const {
        const char	*s;
        bool		found;

        found = get_string( key, defaultString, &s );
        out = atof( s );
        return found;
    }

    /*
    ================
    Dict::get_int
    ================
    */
    bool Dict::get_int( const char *key, const char *defaultString, int &out ) const {
        const char	*s;
        bool		found;

        found = get_string( key, defaultString, &s );
        out = atoi( s );
        return found;
    }

    /*
    ================
    Dict::get_bool
    ================
    */
    bool Dict::get_bool( const char *key, const char *defaultString, bool &out ) const {
        const char	*s;
        bool		found;

        found = get_string( key, defaultString, &s );
        out = ( atoi( s ) != 0 );
        return found;
    }

    /*
    ================
    Dict::get_Angles
    ================
    */
    bool Dict::get_Angles( const char *key, const char *defaultString, Angles &out ) const {
        bool		found;
        const char	*s;

        if ( !defaultString ) {
            defaultString = "0 0 0";
        }

        found = get_string( key, defaultString, &s );
        out.zero();	
        sscanf( s, "%f %f %f", &out.pitch, &out.yaw, &out.roll );
        return found;
    }

    /*
    ================
    Dict::get_Vec3
    ================
    */
    bool Dict::get_Vec3( const char *key, const char *defaultString, Vec3 &out ) const {
        bool		found;
        const char	*s;

        if ( !defaultString ) {
            defaultString = "0 0 0";
        }

        found = get_string( key, defaultString, &s );
        out.zero();
        sscanf( s, "%f %f %f", &out.x, &out.y, &out.z );
        return found;
    }

    /*
    ================
    Dict::get_Vec2
    ================
    */
    bool Dict::get_Vec2( const char *key, const char *defaultString, Vec2 &out ) const {
        bool		found;
        const char	*s;

        if ( !defaultString ) {
            defaultString = "0 0";
        }

        found = get_string( key, defaultString, &s );
        out.zero();
        sscanf( s, "%f %f", &out.x, &out.y );
        return found;
    }

    /*
    ================
    Dict::get_Vec4
    ================
    */
    bool Dict::get_Vec4( const char *key, const char *defaultString, Vec4 &out ) const {
        bool		found;
        const char	*s;

        if ( !defaultString ) {
            defaultString = "0 0 0 0";
        }

        found = get_string( key, defaultString, &s );
        out.zero();
        sscanf( s, "%f %f %f %f", &out.x, &out.y, &out.z, &out.w );
        return found;
    }

    /*
    ================
    Dict::get_Mat3
    ================
    */
    bool Dict::get_Mat3( const char *key, const char *defaultString, Mat3 &out ) const {
        const char	*s;
        bool		found;

        if ( !defaultString ) {
            defaultString = "1 0 0 0 1 0 0 0 1";
        }

        found = get_string( key, defaultString, &s );
        out.identity();		// sccanf has a bug in it on Mac OS 9.  Sigh.
        sscanf( s, "%f %f %f %f %f %f %f %f %f", &out[0].x, &out[0].y, &out[0].z, &out[1].x, &out[1].y, &out[1].z, &out[2].x, &out[2].y, &out[2].z );
        return found;
    }

    /*
    ================
    Dict::get_Color3
    ================
    */
    bool Dict::get_Color3( const char *key, const char *defaultString, Color3 &out ) const {
        const char	*s;
        bool		found;

        if ( !defaultString ) {
            defaultString = "0 0 0";
        }

        found = get_string( key, defaultString, &s );
        out.zero();		// sccanf has a bug in it on Mac OS 9.  Sigh.
        sscanf( s, "%f %f %f", &out.r, &out.g, &out.b );
        return found;
    }

        /*
    ================
    Dict::get_Color4
    ================
    */
    bool Dict::get_Color4( const char *key, const char *defaultString, Color4 &out ) const {
        const char	*s;
        bool		found;

        if ( !defaultString ) {
            defaultString = "0 0 0 0";
        }

        found = get_string( key, defaultString, &s );
        out.zero();		// sccanf has a bug in it on Mac OS 9.  Sigh.
        sscanf( s, "%f %f %f %f", &out.r, &out.g, &out.b, &out.a );
        return found;
    }

    /*
    ================
    Dict::find_key
    ================
    */
    const KeyValue *Dict::find_key( const char *key ) const {
        int i, hash;

        if ( key == NULL || key[0] == '\0' ) {
            warning( "Dict::find_key: empty key" );
            return NULL;
        }

        hash = argHash.generate_key( key, false );
        for ( i = argHash.get_first( hash ); i != HashIndex::NULL_INDEX; i = argHash.get_next( i ) ) {
            if ( args[i].get_key().cmp( key, false ) == 0 ) {
                return &args[i];
            }
        }

        return NULL;
    }

    /*
    ================
    Dict::generate_key
    ================
    */
    int Dict::generate_key( const char *key ) {
        int hash = argHash.generate_key( key, false );
        for ( int i = argHash.get_first( hash ); i != -1; i = argHash.get_next( i ) ) {
            if ( args[i].get_key().cmp( key, false ) == 0 ) {
                return i;
            }
        }

        int keyIndex;

        KeyValue kv;
        kv.key = get_global_keys()->alloc_string( key );
        kv.value = get_global_values()->alloc_string( "" );
        keyIndex = args.append( kv );
        argHash.add( argHash.generate_key( kv.get_key(), false ), keyIndex );

        return keyIndex;
    }

    /*
    ================
    Dict::find_key_index
    ================
    */
    int Dict::find_key_index( const char *key ) const {
        /*	if ( key == NULL || key[0] == '\0' ) {
        Core::warning( "Dict::find_key_index: empty key" );
        return 0;
        }*/

        int hash = argHash.generate_key( key, false );
        for ( int i = argHash.get_first( hash ); i != HashIndex::NULL_INDEX; i = argHash.get_next( i ) ) {
            if ( args[i].get_key().cmp( key, false ) == 0 ) {
                return i;
            }
        }

        return -1;
    }

    /*
    ================
    Dict::delete_key
    ================
    */
    void Dict::delete_key( const char *key ) {
        int hash, i;

        hash = argHash.generate_key( key, false );
        for ( i = argHash.get_first( hash ); i != -1; i = argHash.get_next( i ) ) {
            if ( args[i].get_key().cmp( key, false ) == 0 ) {
                get_global_keys()->free_string( args[i].key );
                get_global_values()->free_string( args[i].value );
                args.remove_index( i );
                argHash.remove_index( hash, i );
                break;
            }
        }

#if 0
        // make sure all keys can still be found in the hash index
        for ( i = 0; i < args.size(); i++ ) {
            assert( find_key( args[i].get_key() ) != NULL );
        }
#endif
    }

    /*
    ================
    Dict::match_prefix
    ================
    */
    const KeyValue *Dict::match_prefix( const char *prefix, const KeyValue *lastMatch ) const {
        int	i;
        int len;
        int start;

        assert( prefix );
        len = Str::length( prefix );

        start = -1;
        if ( lastMatch ) {
            start = args.find_index( *lastMatch );
            assert( start >= 0 );
            if ( start < 1 ) {
                start = 0;
            }
        }

        for( i = start + 1; i < args.size(); i++ ) {
            if ( !args[i].get_key().cmpn( prefix, len, false ) ) {
                return &args[i];
            }
        }
        return NULL;
    }

    /*
    ================
    Dict::random_prefix
    ================
    */
    const char *Dict::random_prefix( const char *prefix, Random &random ) const {
        int count;
        const int MAX_RANDOM_KEYS = 2048;
        const char *list[MAX_RANDOM_KEYS];
        const KeyValue *kv;

        list[0] = "";
        for ( count = 0, kv = match_prefix( prefix ); kv && count < MAX_RANDOM_KEYS; kv = match_prefix( prefix, kv ) ) {
            list[count++] = kv->get_value().c_str();
        }
        return list[random.random_int( count )];
    }



    /*
    ================
    Dict::init
    ================
    */
    //void Dict::init( void ) {
    //	if ( !globalKeys ) {
    //		globalKeys = new StrPool( 1024 );
    //		globalKeys->set_case_sensitive( false );
    //	}
    //
    //	if ( !globalValues ) {
    //		globalValues = new StrPool( 1024 );
    //		globalValues->set_case_sensitive( true );
    //	}
    //}

    /*
    ================
    Dict::shutdown
    ================
    */
    //void Dict::shutdown( void ) {
    //	if ( globalKeys ) {
    //		globalKeys->clear();
    //		delete globalKeys;
    //		globalKeys = NULL;
    //	}
    //
    //	if ( globalValues ) {
    //		globalValues->clear();
    //		delete globalValues;
    //		globalValues = NULL;
    //	}
    //}

    struct GlobalKeysWrap {
        GlobalKeysWrap(int preAllocNum) : globalKeys(preAllocNum) {
            globalKeys.set_case_sensitive( false );
        }

        ~GlobalKeysWrap() {
            globalKeys.clear();
        }

        StrPool globalKeys;
    };

    struct GlobalValuesWrap {
        GlobalValuesWrap(int preAllocNum) : globalValues(preAllocNum) {
            globalValues.set_case_sensitive( true );
        }

        ~GlobalValuesWrap() {
            globalValues.clear();
        }

        StrPool globalValues;
    };


    StrPool*	Dict::get_global_keys() {
        static GlobalKeysWrap globalKeysW(1024);

        return &( globalKeysW.globalKeys );
    }

    StrPool*	Dict::get_global_values() {
        static GlobalValuesWrap globalValuesW(1024);

        return &( globalValuesW.globalValues );
    }

    /*
    ================
    Dict::ShowMemoryUsage_f
    ================
    */
    //void Dict::ShowMemoryUsage_f( const idCmdArgs &args ) {
    //	Core::printf( "%5d KB in %d keys\n", globalKeys->total_size() >> 10, globalKeys->size() );
    //	Core::printf( "%5d KB in %d values\n", globalValues->total_size() >> 10, globalValues->size() );
    //}

    /*
    ================
    idDictStringSortCmp
    ================
    */
    // NOTE: the const wonkyness is required to make msvc happy
    template<>
    int listSortCompare( const PoolStr * const *a, const PoolStr * const *b ) {
        return (*a)->cmp( **b, false );
    }

    /*
    ============
    Dict::SetGlobalPools
    ============
    */
    //void Dict::SetGlobalPools( StrPool* _globalKeys, StrPool* _globalValues ) {
    //	if( globalValues || globalKeys ) {
    //		return;
    //	}
    ///*
    //	assert( globalKeys == NULL );
    //	assert( globalValues == NULL );
    //*/
    //	globalKeys		= _globalKeys;
    //	globalValues	= _globalValues;
    //}

    /*
    ============
    Dict::GetGlobalPools
    ============
    */
    //void Dict::GetGlobalPools( StrPool*& _globalKeys, StrPool*& _globalValues ) {
    //	_globalKeys		= globalKeys;
    //	_globalValues	= globalValues;
    //}

}   // ::inf
